/*--- rle1.c ------------------------------ Listing 9-1 ---------
 *  Run-Length Encoding for files of all types
 *
 *-------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>

#define Sentinel    0xF0   /* the sentinel flag */
#define BUFFER_SIZE 30000

#define WriteCode(a,b,c) \
            fprintf( outfile, "%c%c%c", a, b, c )

FILE *infile,
     *outfile;

int main(int argc, char *argv[])
{
    unsigned char *buffer;
    unsigned char prev_char;
    int  bytes_read,
         count,
         eof,
         first_time,
         i;

    if (argc != 3) {
        fprintf(stderr, "Performs standard RLE compression\n"
                        "Usage: rle1 infile outfile\n");
        return (EXIT_FAILURE);
    }

    if ((infile = fopen(argv[1], "rb")) == NULL) {
        fprintf(stderr, "Error opening %s\n", argv[1]);
        return (EXIT_FAILURE);
    }

    if ((outfile = fopen(argv[2], "wb")) == NULL)
    {
        fprintf(stderr, "Error opening %s\n", argv[2]);
        return (EXIT_FAILURE);
    }

    /* write the header to the output file */
    fprintf(outfile, "%c%c%c%c", 'R', 'L', '1', Sentinel);

    /* initialize the necessary variables */
    eof = 0;
    first_time = 1;

    buffer = (unsigned char *)malloc(BUFFER_SIZE);
    if (buffer == NULL) {
        fprintf(stderr, "Unable to allocate %d bytes\n",
                BUFFER_SIZE);
        return (EXIT_FAILURE);
    }

    /* process the input file */
    while (!eof) {
        bytes_read = fread(buffer, 1, BUFFER_SIZE, infile);
        if (bytes_read == 0) {
            eof = 1;
            break;
        }

        for (i = 0; i < bytes_read; i++) {
            /* first time through is a special case */
            if (first_time) {
                prev_char = buffer[i];
                count = 1;
                first_time = 0;
                i++;
            }

            if (buffer[i] == prev_char) {  /* repeated char */
                count += 1;
                if (count == 255) {
                    WriteCode(Sentinel, count, prev_char);
                    count = 0;
                }
                continue;
            } else {   /* a new char, so write out all known data */
                if (count < 3) {
                    if (prev_char == Sentinel) {
                        fprintf(outfile, "%c%c", Sentinel, count);
                    } else {
                        do {
                            fputc(prev_char, outfile);
                        } while (--count);
                    }
                } else
                    WriteCode(Sentinel, count, prev_char);

                prev_char = buffer[i];
                count = 1;
            }
        }

        /* we're at end of bytes_read, is it EOF? */
        if (bytes_read < BUFFER_SIZE)
            eof = 1;
    }

    /* at EOF, so flush out any remaining bytes to be written */
    if (count < 3) {
        if (prev_char == Sentinel) {
            fprintf(outfile, "%c%c", Sentinel, count);
        } else {
            do {
                fputc(prev_char, outfile);
            } while (--count);
        }
    } else
        WriteCode(Sentinel, count, prev_char);

    fclose(infile);
    fclose(outfile);

    return (EXIT_SUCCESS);
}
